bitkeeper revision 1.1159.1.27 (41180722W5BkKRn14WoivvfF9m_vHw)
authorkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Mon, 9 Aug 2004 23:22:10 +0000 (23:22 +0000)
committerkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Mon, 9 Aug 2004 23:22:10 +0000 (23:22 +0000)
vmalloc fix for XL2.4

.rootkeys
linux-2.4.26-xen-sparse/mm/vmalloc.c [new file with mode: 0644]

index 2139d8b61ec0bf6f581802d6d6d10cbaa17f531f..adfd56a2862b664263e51b391c83ff4509d3a5a8 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3e5a4e681xMPdF9xCMwpyfuYMySU5g linux-2.4.26-xen-sparse/mm/mremap.c
 409ba2e7akOFqQUg6Qyg2s28xcXiMg linux-2.4.26-xen-sparse/mm/page_alloc.c
 3e5a4e683HKVU-sxtagrDasRB8eBVw linux-2.4.26-xen-sparse/mm/swapfile.c
+41180721bNns9Na7w1nJ0ZVt8bhUNA linux-2.4.26-xen-sparse/mm/vmalloc.c
 40f562372u3A7_kfbYYixPHJJxYUxA linux-2.6.7-xen-sparse/arch/xen/Kconfig
 40f56237utH41NPukqHksuNf29IC9A linux-2.6.7-xen-sparse/arch/xen/Kconfig.drivers
 40f56237penAAlWVBVDpeQZNFIg8CA linux-2.6.7-xen-sparse/arch/xen/Makefile
diff --git a/linux-2.4.26-xen-sparse/mm/vmalloc.c b/linux-2.4.26-xen-sparse/mm/vmalloc.c
new file mode 100644 (file)
index 0000000..812b214
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ *  linux/mm/vmalloc.c
+ *
+ *  Copyright (C) 1993  Linus Torvalds
+ *  Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999
+ *  SMP-safe vmalloc/vfree/ioremap, Tigran Aivazian <tigran@veritas.com>, May 2000
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/spinlock.h>
+#include <linux/highmem.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+
+rwlock_t vmlist_lock = RW_LOCK_UNLOCKED;
+struct vm_struct * vmlist;
+
+static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size)
+{
+       pte_t * pte;
+       unsigned long end;
+
+       if (pmd_none(*pmd))
+               return;
+       if (pmd_bad(*pmd)) {
+               pmd_ERROR(*pmd);
+               pmd_clear(pmd);
+               return;
+       }
+       pte = pte_offset(pmd, address);
+       address &= ~PMD_MASK;
+       end = address + size;
+       if (end > PMD_SIZE)
+               end = PMD_SIZE;
+       do {
+               pte_t page;
+               page = ptep_get_and_clear(pte);
+               address += PAGE_SIZE;
+               pte++;
+               if (pte_none(page))
+                       continue;
+               if (pte_present(page)) {
+                       struct page *ptpage = pte_page(page);
+                       if (VALID_PAGE(ptpage) && (!PageReserved(ptpage)))
+                               __free_page(ptpage);
+                       continue;
+               }
+               printk(KERN_CRIT "Whee.. Swapped out page in kernel page table\n");
+       } while (address < end);
+}
+
+static inline void free_area_pmd(pgd_t * dir, unsigned long address, unsigned long size)
+{
+       pmd_t * pmd;
+       unsigned long end;
+
+       if (pgd_none(*dir))
+               return;
+       if (pgd_bad(*dir)) {
+               pgd_ERROR(*dir);
+               pgd_clear(dir);
+               return;
+       }
+       pmd = pmd_offset(dir, address);
+       address &= ~PGDIR_MASK;
+       end = address + size;
+       if (end > PGDIR_SIZE)
+               end = PGDIR_SIZE;
+       do {
+               free_area_pte(pmd, address, end - address);
+               address = (address + PMD_SIZE) & PMD_MASK;
+               pmd++;
+       } while (address < end);
+}
+
+void vmfree_area_pages(unsigned long address, unsigned long size)
+{
+       pgd_t * dir;
+       unsigned long end = address + size;
+
+       dir = pgd_offset_k(address);
+       flush_cache_all();
+       do {
+               free_area_pmd(dir, address, end - address);
+               address = (address + PGDIR_SIZE) & PGDIR_MASK;
+               dir++;
+       } while (address && (address < end));
+       flush_tlb_all();
+}
+
+static inline int alloc_area_pte (pte_t * pte, unsigned long address,
+                       unsigned long size, int gfp_mask,
+                       pgprot_t prot, struct page ***pages)
+{
+       unsigned long end;
+
+       address &= ~PMD_MASK;
+       end = address + size;
+       if (end > PMD_SIZE)
+               end = PMD_SIZE;
+       do {
+               struct page * page;
+
+               if (!pages) {
+                       spin_unlock(&init_mm.page_table_lock);
+                       page = alloc_page(gfp_mask);
+                       spin_lock(&init_mm.page_table_lock);
+               } else {
+                       page = (**pages);
+                       (*pages)++;
+
+                       /* Add a reference to the page so we can free later */
+                       if (page)
+                               atomic_inc(&page->count);
+
+               }
+               if (!pte_none(*pte))
+                       printk(KERN_ERR "alloc_area_pte: page already exists\n");
+               if (!page)
+                       return -ENOMEM;
+               set_pte(pte, mk_pte(page, prot));
+               address += PAGE_SIZE;
+               pte++;
+       } while (address < end);
+       return 0;
+}
+
+static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address,
+                       unsigned long size, int gfp_mask,
+                       pgprot_t prot, struct page ***pages)
+{
+       unsigned long end;
+
+       address &= ~PGDIR_MASK;
+       end = address + size;
+       if (end > PGDIR_SIZE)
+               end = PGDIR_SIZE;
+       do {
+               pte_t * pte = pte_alloc(&init_mm, pmd, address);
+               if (!pte)
+                       return -ENOMEM;
+               if (alloc_area_pte(pte, address, end - address,
+                                       gfp_mask, prot, pages))
+                       return -ENOMEM;
+               address = (address + PMD_SIZE) & PMD_MASK;
+               pmd++;
+       } while (address < end);
+       return 0;
+}
+
+static inline int __vmalloc_area_pages (unsigned long address,
+                                       unsigned long size,
+                                       int gfp_mask,
+                                       pgprot_t prot,
+                                       struct page ***pages)
+{
+       pgd_t * dir;
+       unsigned long start = address;
+       unsigned long end = address + size;
+
+       dir = pgd_offset_k(address);
+       spin_lock(&init_mm.page_table_lock);
+       do {
+               pmd_t *pmd;
+               
+               pmd = pmd_alloc(&init_mm, dir, address);
+               if (!pmd)
+                       goto err;
+
+               if (alloc_area_pmd(pmd, address, end - address, gfp_mask, prot, pages))
+                       goto err;       // The kernel NEVER reclaims pmds, so no need to undo pmd_alloc() here
+
+               address = (address + PGDIR_SIZE) & PGDIR_MASK;
+               dir++;
+       } while (address && (address < end));
+       spin_unlock(&init_mm.page_table_lock);
+       flush_cache_all();
+       XEN_flush_page_update_queue();
+       return 0;
+err:
+       spin_unlock(&init_mm.page_table_lock);
+       flush_cache_all();
+       if (address > start)
+               vmfree_area_pages(start, address - start);
+       return -ENOMEM;
+}
+
+int vmalloc_area_pages(unsigned long address, unsigned long size,
+                      int gfp_mask, pgprot_t prot)
+{
+       return __vmalloc_area_pages(address, size, gfp_mask, prot, NULL);
+}
+
+struct vm_struct * get_vm_area(unsigned long size, unsigned long flags)
+{
+       unsigned long addr, next;
+       struct vm_struct **p, *tmp, *area;
+
+       area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
+       if (!area)
+               return NULL;
+
+       size += PAGE_SIZE;
+       if (!size) {
+               kfree (area);
+               return NULL;
+       }
+
+       addr = VMALLOC_START;
+       write_lock(&vmlist_lock);
+       for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
+               if ((size + addr) < addr)
+                       goto out;
+               if (size + addr <= (unsigned long) tmp->addr)
+                       break;
+               next = tmp->size + (unsigned long) tmp->addr;
+               if (next > addr) 
+                       addr = next;
+               if (addr > VMALLOC_END-size)
+                       goto out;
+       }
+       area->flags = flags;
+       area->addr = (void *)addr;
+       area->size = size;
+       area->next = *p;
+       *p = area;
+       write_unlock(&vmlist_lock);
+       return area;
+
+out:
+       write_unlock(&vmlist_lock);
+       kfree(area);
+       return NULL;
+}
+
+void __vfree(void * addr, int free_area_pages)
+{
+       struct vm_struct **p, *tmp;
+
+       if (!addr)
+               return;
+       if ((PAGE_SIZE-1) & (unsigned long) addr) {
+               printk(KERN_ERR "Trying to vfree() bad address (%p)\n", addr);
+               return;
+       }
+       write_lock(&vmlist_lock);
+       for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
+               if (tmp->addr == addr) {
+                       *p = tmp->next;
+                       if (free_area_pages)
+                               vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
+                       write_unlock(&vmlist_lock);
+                       kfree(tmp);
+                       return;
+               }
+       }
+       write_unlock(&vmlist_lock);
+       printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n", addr);
+}
+
+void vfree(void * addr)
+{
+       __vfree(addr,1);
+}
+
+void * __vmalloc (unsigned long size, int gfp_mask, pgprot_t prot)
+{
+       void * addr;
+       struct vm_struct *area;
+
+       size = PAGE_ALIGN(size);
+       if (!size || (size >> PAGE_SHIFT) > num_physpages)
+               return NULL;
+       area = get_vm_area(size, VM_ALLOC);
+       if (!area)
+               return NULL;
+       addr = area->addr;
+       if (__vmalloc_area_pages(VMALLOC_VMADDR(addr), size, gfp_mask,
+                                prot, NULL)) {
+               __vfree(addr, 0);
+               return NULL;
+       }
+       return addr;
+}
+
+void * vmap(struct page **pages, int count,
+           unsigned long flags, pgprot_t prot)
+{
+       void * addr;
+       struct vm_struct *area;
+       unsigned long size = count << PAGE_SHIFT;
+
+       if (!size || size > (max_mapnr << PAGE_SHIFT))
+               return NULL;
+       area = get_vm_area(size, flags);
+       if (!area) {
+               return NULL;
+       }
+       addr = area->addr;
+       if (__vmalloc_area_pages(VMALLOC_VMADDR(addr), size, 0,
+                                prot, &pages)) {
+               __vfree(addr, 0);
+               return NULL;
+       }
+       return addr;
+}
+
+long vread(char *buf, char *addr, unsigned long count)
+{
+       struct vm_struct *tmp;
+       char *vaddr, *buf_start = buf;
+       unsigned long n;
+
+       /* Don't allow overflow */
+       if ((unsigned long) addr + count < count)
+               count = -(unsigned long) addr;
+
+       read_lock(&vmlist_lock);
+       for (tmp = vmlist; tmp; tmp = tmp->next) {
+               vaddr = (char *) tmp->addr;
+               if (addr >= vaddr + tmp->size - PAGE_SIZE)
+                       continue;
+               while (addr < vaddr) {
+                       if (count == 0)
+                               goto finished;
+                       *buf = '\0';
+                       buf++;
+                       addr++;
+                       count--;
+               }
+               n = vaddr + tmp->size - PAGE_SIZE - addr;
+               do {
+                       if (count == 0)
+                               goto finished;
+                       *buf = *addr;
+                       buf++;
+                       addr++;
+                       count--;
+               } while (--n > 0);
+       }
+finished:
+       read_unlock(&vmlist_lock);
+       return buf - buf_start;
+}
+
+long vwrite(char *buf, char *addr, unsigned long count)
+{
+       struct vm_struct *tmp;
+       char *vaddr, *buf_start = buf;
+       unsigned long n;
+
+       /* Don't allow overflow */
+       if ((unsigned long) addr + count < count)
+               count = -(unsigned long) addr;
+
+       read_lock(&vmlist_lock);
+       for (tmp = vmlist; tmp; tmp = tmp->next) {
+               vaddr = (char *) tmp->addr;
+               if (addr >= vaddr + tmp->size - PAGE_SIZE)
+                       continue;
+               while (addr < vaddr) {
+                       if (count == 0)
+                               goto finished;
+                       buf++;
+                       addr++;
+                       count--;
+               }
+               n = vaddr + tmp->size - PAGE_SIZE - addr;
+               do {
+                       if (count == 0)
+                               goto finished;
+                       *addr = *buf;
+                       buf++;
+                       addr++;
+                       count--;
+               } while (--n > 0);
+       }
+finished:
+       read_unlock(&vmlist_lock);
+       return buf - buf_start;
+}